<script setup lang="ts">
import {inject, onBeforeMount, ref, useSlots} from "vue"
import {NFormItem} from 'naive-ui'
import {
  IptPropsType,
  FormItemPropsType,
  StnIptPropsFieldType,
  StnIptPredefinePropsFieldType,
} from './type.ts'
import {IptTypeEnum} from './enums/ipt-type-enum.ts'
import {IptTypeMapConst} from './consts/ipt-type-map-const.ts'
import {StnIptPropsFieldDefault} from './consts/stn-ipt-const.ts'

/**
 * 组件配置选项
 */
defineOptions({
  name: 'StnIpt',
})

/**
 * 组件参数
 */
const {
  formItem = true,
  type = IptTypeEnum.INPUT,
  predefineProps = StnIptPropsFieldDefault,
  props = StnIptPropsFieldDefault,
  clearable = true,
} = defineProps<{
  // 是否使用表单项组件
  formItem?: boolean;
  // 输入组件的类型
  type?: IptTypeEnum;
  // 组件预定义 Props 的获取路径字符串或获取函数或 props 对象
  predefineProps?: StnIptPredefinePropsFieldType;
  // 组件 Props
  props?: StnIptPropsFieldType;
  // 是否可清空
  clearable?: boolean;
}>()

/**
 * 组件双向绑定
 */
const value = defineModel<any>('value')

/**
 * 组件插槽
 */
const slots = useSlots()

/**
 * 注入父组件提供的数据
 */
const predefinedPropsFromParent = inject<Record<string, any>>('predefinedProps', StnIptPropsFieldDefault)

// 输入组件 Props
const iptProps = ref<IptPropsType>({})
// 表单项组件 Props
const formItemProps = ref<FormItemPropsType>({})
/**
 * 获取预定义 Props
 */
const getPredefineProps = (): StnIptPropsFieldType => {
  if (typeof predefineProps === 'string') {
    // 获取预定义 Props 的字段路径数组
    const pathArr = predefineProps.split('.')
    // 最终获取的预定义属性
    let result = predefinedPropsFromParent
    for (const path of pathArr) {
      // 如果属性不存在
      if (!Reflect.has(result, path)) return StnIptPropsFieldDefault
      result = result[path]
    }
    return result ? result as StnIptPropsFieldType : StnIptPropsFieldDefault
  } else if (typeof predefineProps === 'function') {
    return predefineProps()
  } else {
    return predefineProps
  }
}
onBeforeMount(() => {
  // 获取预定义 Props
  const predefinePropsRes = getPredefineProps()
  iptProps.value = predefinePropsRes.ipt ?? StnIptPropsFieldDefault.ipt
  formItemProps.value = predefinePropsRes.formItem ?? StnIptPropsFieldDefault.formItem
})
</script>

<template>
  <n-form-item
    v-if="formItem"
    v-bind="{
      ...formItemProps,
      ...(props.formItem ?? {}),
    }"
  >
    <component
      :is="IptTypeMapConst[type]"
      v-bind="{
        ...iptProps,
        options: iptProps.getOptions ? iptProps.getOptions() : iptProps.options,
        ...(props.ipt ?? {}),
        clearable,
        value,
        'onUpdate:value': (val: any) => value = val,
      }"
    >
      <template
        v-for="(_, name) in slots"
        #[name]
      >
        <slot :name="name"></slot>
      </template>
    </component>
  </n-form-item>
  <component
    v-else
    :is="IptTypeMapConst[type]"
    v-bind="{
      ...iptProps,
      options: iptProps.getOptions ? iptProps.getOptions() : iptProps.options,
      ...(props.ipt ?? {}),
      clearable,
      value,
      'onUpdate:value': (val: any) => value = val,
    }"
  >
    <template
      v-for="(_, name) in slots"
      #[name]
    >
      <slot :name="name"></slot>
    </template>
  </component>
</template>

<style scoped>
.stn-ipt {
  color: inherit;
}
</style>
